前端的部份會著重在JS邏輯上面,JS會使用jquery,美化排版等css有使用bootstra不過不會多談,而且我會盡量把bootstrap的部份拔掉。
我的style.css檔案
.hide {
display: none;
}
body {
width: 100%;
background: whitesmoke;
}
.fixed-center {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: 0 auto;
}
.abs-center-H {
position: absolute;
left: 50%;
transform: translate(-50%, 0);
margin: 0 auto;
}
.tui-editor-contents {
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
font-size: 1rem;
}
先把登錄窗口建起來,進入view/html/component創建signContainer寫入
{{define "container/sign"}}
<div id="singContainer" style="border-width:3px;border-style:solid;border-color:#000000;padding:5px;display:block;">
<article>
<div>
<button id="closeSignBtn" type="button">×</button>
<h4>Sign in</h4>
<form id="SignForm" action="http://account.dcreater.com/login" method="post">
<div>
<label>Your user name</label>
<input id="InputUserName" name="" class="form-control" type="text">
</div>
<div>
<label>Your password</label>
<input id="InputPassword" class="form-control" placeholder="******" type="password">
</div>
<p id="AlertWrongParam" class="text-danger" style="display:none;">Wrong user name or password!!</p>
<button id="SigninSubmitBtn" class="btn btn-primary btn-block" type="submit">
Sign in
</button>
No account? <a href="#">Sign up</a>
</form>
</div>
</article>
</div>
{{end}}
進入js創建sign.js寫入
// this is the id of the form
$("#SignForm").submit(function(e) {
let url = $(this).attr('action');
let username = $("#InputUserName").val();
let password = $("#InputPassword").val();
let data = new FormData();
data.append('username', username);
data.append('password', password);
$.ajax({
method: "POST",
url: url,
data: data,
processData: false,
contentType: false,
xhrFields: {
withCredentials: true
},
success: function(data){
SetUserCookie(data.user)
window.location.reload();
},
error: function(jqXHR, textStatus, errorThrown){
console.log(jqXHR)
console.log(textStatus)
console.log(errorThrown)
$("#AlertWrongParam").show();
}
});
e.preventDefault(); // avoid to execute the actual submit of the form.
});
// click on sign in
$("#SigninBtn").on("click", function(){
$("#singContainer").show();
});
// click on x
$("#closeSignBtn").on("click", function(){
HideSignContainer()
});
function HideSignContainer() {
$("#singContainer").hide();
}
function SetUserCookie(user) {
let sub = [["text", "subOid"], ["text", "subOuniquename"],["text", "subOnickname"],["text", "subOdescription"]];
split_strs(user, sub)
document.cookie = "OwnerList=" + JSON.stringify(user) + ";Path=/;domain=.dcreater.com;Max-Age=2592000;SameSite=Lax";
}
然後是導覽列,創建navigation.html寫入
{{ define "navigation"}}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
<div class="container">
<a href="/">DCreater</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item active hide">
<a href="#">Home
<span class="sr-only">(current)</span>
</a>
</li>
<li id="UpdateBlogNav" class="nav-item active">
<a class="nav-link" href="#UpdateBlog">編輯專案</a>
</li>
<li id="DeleteBlogNav" class="nav-item active">
<button type="button" class="btn btn-dark" id="DeleteBlogBtn">刪除專案</button>
</li>
<li id="NewBlogNav" class="nav-item active">
<a class="nav-link" href="#NewBlog">新專案</a>
</li>
<li id="UpdateOwnerNav" class="nav-item active">
<a class="nav-link" href="/#UpdateOwner">編輯身份</a>
</li>
<li id="DeleteOwnerNav" class="nav-item active">
<button type="button" class="btn btn-dark" id="DeleteOwnerBtn">刪除身份</button>
</li>
<li id="NewOwnerNav" class="nav-item active">
<a class="nav-link" href="/#NewOwner">新身份</a>
</li>
<li id="ReturnNav" class="nav-item active hide">
<a class="nav-link" href="">返回</a>
</li>
<li >
<button id="SigninBtn" type="button">登入</button>
</li>
</ul>
</div>
</div>
</nav>
{{ end }}
在meta/index.html補上
<!-- Navigation -->
{{template "navigation" . }}
<!-- signin window -->
{{template "container/sign"}}
在js創建main.js寫入
blogTypeInvert = {
1: "project",
2: "article"
};
let filler = {
"blog-list-owner": ["text", "onickname"],
"owner-href": ["href", "ouniquename"],
};
// global
var meta = JSON.parse($("#meta").text());
let path = window.location.pathname.split("/").slice(1);
if (window.location.pathname === "/") {
rend_root();
} else {
rend_blog();
rend_content();
}
解釋:
rend_root
function rend_root() {
let sub = {
"blog-list-title": ["text", "bname"],
"blog-list-description": ["text", "bdescription"],
"blog-list-createtime": ["text", "bcreatetime"],
"blog-href": ["href", "burlpath"],
};
let result = "";
for (let i = 0; i < meta.length; i++) {
result += fill_content("#blog-list-tmpl", meta[i], Object.assign(filler, sub), 1);
}
$("div.blog-list").html(result);
}
// get template and fill with data
function fill_content(tmplid, data, filler, len) {
let tmpl = get_tmpl(tmplid);
if (tmpl.length === 0) {
return ""
}
let result = "";
for (let i = 0; i < len; i++) {
fill_tmpl(tmpl, data, filler, i);
result += tmpl[0].outerHTML;
}
return result
}
function get_tmpl(target) {
let html = $(target).html();
return $(html).clone();
}
function fill_tmpl(tmpl, data, filler, index) {
let value = "";
for (let [k, v] of Object.entries(filler)) {
if (Array.isArray(data[v[1]])) {
value = data[v[1]][index];
} else {
value = data[v[1]];
}
if (v[0] === "text") {
fill_text(tmpl.find("." + k), value);
} else {
fill_attr(tmpl.find("." + k), v[0], value);
}
}
}
function fill_text(node, value) {
node.text(value);
}
function fill_attr(node, attr, value) {
if (attr === "href") {
fill_href(node, value);
} else {
node.attr(attr, value);
}
}
function fill_href(node, value) {
if (window.location.pathname !== "/" && node.attr("class") === "blog-href") {
value = window.location.pathname + "/" + value;
}
if (value[0] !== "/") {
value = "/" + value;
}
node.attr("href", value);
}
解釋:
read_content會處理blog內容的渲染,明天會提到
還需要處理owner,在meta/index.html補上
{{template "form/owner"}}
在component創建ownerForm.html寫上
{{ define "form/owner"}}
<div id="formContainer" class="container my-5">
<div class="row">
<div class="col">
<div class="card mt-3">
<div class="card-body">
<form id="NewOwnerForm" style="display: block;">
<div class="form-group">
<label for="NewOwnerName">Owner Name</label>
<input id="NewOwnerName" type="text" class="form-control" placeholder="限制50字(不可重複)">
<p class="text-danger invisible">超過50字</p>
<label for="NewNickName">暱稱</label>
<input id="NewNickName" type="text" class="form-control" placeholder="限制50字(必填)">
<p class="text-danger invisible">超過50字</p>
<label for="NewOwnerDescription">簡述</label>
<input id="NewOwnerDescription" type="text" class="form-control" placeholder="簡單自我介紹 (限制255字)">
<p class="text-danger invisible">超過255字</p>
<button id="OwnerSubmitBtn" type="submit" class="float-right">Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{{ end }}
在view/js創建owner.js寫入
//global
var ownerList
// rend for login user
let OwnerSelectNav = $("#OwnerSelectNav");
rend_owner();
// rend for owner list
function rend_owner() {
ownerList = check_login();
if (ownerList === undefined) {
show_login();
return;
}
for(let i = 0; i < ownerList.subOid.length; i++){
OwnerSelectNav.append("<option value=\"" + ownerList.subOid[i] + "\">" + ownerList.subOnickname[i] + "</option>")
}
OwnerSelectNav.removeClass("hide");
OwnerSelectNav.val(ownerList.subOid[0]);
hide_login();
}
// return owner data if user has login, else return undefined
function check_login(){
let ownerList = Cookies.get('OwnerList');
if (ownerList !== undefined) {
ownerList = JSON.parse(ownerList)
}
return ownerList;
}
// show login btn
function show_login(){
$("#LoginNav").show();
}
// hide login btn
function hide_login(){
$("#LoginNav").hide();
}
創建account.js寫入
// global
let oid, ownername, nickname, descryption
if(ownerList !== undefined){
SetNowOwner(ownerList.subOid[0]);
OwnerSelectNav.removeClass("hide");
}
// check hashtag
HandleNewOwner();
// owner list on change
OwnerSelectNav.on('change', function() {
SetNowOwner(OwnerSelectNav.val());
FillForm();
});
function SetNowOwner(in_id) {
oid = in_id;
let i = ownerList.subOid.indexOf(oid);
ownername = ownerList.subOuniquename[i];
nickname = ownerList.subOnickname[i];
descryption = i < ownerList.subOdescription.length ? ownerList.subOdescription[i] : "";
}
// hashtag event
$(window).on('hashchange', function () {
HandleNewOwner();
});
function HandleNewOwner() {
// editflag that create editor
let editFlag = ["#NewOwner", "#UpdateOwner"];
if (editFlag.includes(window.location.hash)) {
HandleShowOwnerForm();
} else {
HandleHideOwnerForm();
}
}
$("#DeleteOwnerBtn").on("click", function() {
let oid = OwnerSelectNav.val();
let data = new FormData();
data.append("oid", oid);
$.ajax({
url: "/" + ownerList.subOuniquename[ownerList.subOid.indexOf(oid)],
method: 'DELETE',
data: data,
cache: false,
contentType: false,
processData: false,
withCredentials: true,
success: function(result) {
$("option[value=\"" + oid + "\"]").remove();
alert("刪除成功");
},
error: function(jqXHR, textStatus, errorThrown){
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
alert("刪除失敗,請稍後再試");
},
});
});
// create a new owner or update exist owner
$("#OwnerSubmitBtn").on("click", function (e) {
let method = "post";
let in_name = $("#NewOwnerName").val();
let data = new FormData();
data.append("nickname", $("#NewNickName").val());
data.append("descript", $("#NewOwnerDescription").val());
data.append("oid", oid);
if (window.location.hash === "#UpdateOwner") {
// update
data.append("newuniname", (in_name === ownername ? "" : in_name));
method = 'put';
}
$.ajax({
url: "/" + (window.location.hash === "#NewOwner" ? in_name : ownername),
data: data,
cache: false,
contentType: false,
processData: false,
method: method,
withCredentials: true,
success: function (data) {
// redirect
window.location.href = "https://dcreater.com";
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
alert("sorry, something error. Please try again later");
}
});
e.preventDefault();
});
function HandleShowOwnerForm() {
HideBlogList();
ShowReturnBtn();
HideNewOwnerBtn();
ShowForm();
FillForm();
}
function HandleHideOwnerForm() {
ShowBlogList();
HideReturnBtn();
ShowNewOwnerBtn();
HideForm();
}
function FillForm() {
if(window.location.hash === "#NewOwner") {
return;
}
$("#NewOwnerName").val(ownername);
$("#NewNickName").val(nickname);
$("#NewOwnerDescription").val(descryption);
}
// show Owner or project form
function ShowForm() {
$("#formContainer").show();
}
// Hide Owner or project form
function HideForm() {
$("#formContainer").hide();
}
function HideBlogList() {
$("main.container").hide();
}
function ShowBlogList() {
$("main.container").show();
}
function ShowReturnBtn() {
$("#ReturnNav").show();
}
function HideReturnBtn() {
$("#ReturnNav").hide();
}
function ShowNewOwnerBtn() {
$("#NewOwnerNav").show();
$("#UpdateOwnerNav").show();
}
function HideNewOwnerBtn() {
$("#NewOwnerNav").hide();
$("#UpdateOwnerNav").hide();
}
記得在index.html引入
<!--local script-->
<script src="https://asset.dcreater.com/static/js/owner.js"></script>
<script src="https://asset.dcreater.com/static/js/main.js"></script>
<script src="https://asset.dcreater.com/static/js/sign.js"></script>
<script src="https://asset.dcreater.com/static/js/account.js"></script>
把首頁做出來了,明天會寫讓使用者寫文章的線上編輯器
目前的工作環境
.
├── app
│ ├── apperr
│ │ ├── error.go
│ │ └── handle.go
│ ├── common
│ │ └── cookie.go
│ ├── config
│ │ └── app
│ │ ├── app.yaml
│ │ └── error.yaml
│ ├── database
│ │ ├── auth.go
│ │ ├── connect.go
│ │ ├── error.go
│ │ ├── main.go
│ │ └── scheme.go
│ ├── go.mod
│ ├── go.sum
│ ├── log
│ │ ├── logger.go
│ │ └── logging.go
│ ├── main.go
│ ├── middleware
│ │ ├── auth.go
│ │ ├── error.go
│ │ └── log.go
│ ├── router
│ │ ├── account.go
│ │ ├── asset.go
│ │ ├── host_switch.go
│ │ └── main.go
│ ├── serve
│ │ ├── account.go
│ │ ├── asset.go
│ │ ├── auth.go
│ │ ├── main.go
│ │ └── main_test.go
│ ├── setting
│ │ └── setting.go
│ ├── util
│ │ ├── debug
│ │ │ ├── stack.go
│ │ │ └── stack_test.go
│ │ ├── file
│ │ │ └── file.go
│ │ ├── hash
│ │ │ ├── hash.go
│ │ │ └── hash_test.go
│ │ └── random
│ │ └── random.go
│ └── view
│ ├── css
│ ├── html
│ │ ├── component
│ │ │ ├── blogContainer.html
│ │ │ ├── blogList.html
│ │ │ ├── navigation.html
│ │ │ ├── ownerForm.html
│ │ │ └── signContainer.html
│ │ └── meta
│ │ ├── head.html
│ │ └── index.html
│ └── js
│ ├── account.js
│ ├── main.js
│ ├── owner.js
│ └── sign.js
└── database
└── maindata